// 版权归Go作者所有。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// /+build js，wasm 

// 当使用js/wasm体系结构时，包js允许访问WebAssembly主机环境。
// 其API基于JavaScript语义。
// 
// 这个软件包是实验性的。它目前的范围只是允许运行测试，但还没有为用户提供全面的API。它不受Go兼容性承诺的约束。
package js

import (
	"runtime"
	"unsafe"
)

// ref用于标识JavaScript值，因为该值本身不能传递给WebAssembly。
// 
// JavaScript值“未定义”由值0表示。
// JavaScript数（64位浮点，0和NaN除外）由其IEEE 754二进制表示表示。
// 所有其他值表示为NaN的IEEE 754二进制表示，位0-31用作
// ID，位32-34用于区分字符串、符号、函数和对象。
type ref uint64

// nanHead是ref的高32位，如果该值未编码为IEEE 754编号，则会设置该值（见上文）。
const nanHead = 0x7FF80000

// 包装器由JavaScript值支持的类型实现。
type Wrapper interface {
	// JSValue返回与对象关联的JavaScript值。
	JSValue() Value
}

// 值表示一个JavaScript值。零值是JavaScript值“未定义”。
// 可以使用Equal方法检查值是否相等。
type Value struct {
	_     [0]func() // 不可弥补；要使==不编译
	ref   ref       // 标识JavaScript值，请参阅ref类型
	gcPtr *ref      // 用于在不再引用该值时触发终结器
}

const (
	// 类型标志需要与wasm_exec.js 
	typeFlagNone = iota
	typeFlagObject
	typeFlagString
	typeFlagSymbol
	typeFlagFunction
)

// JSValue实现包装器接口。
func (v Value) JSValue() Value {
	return v
}

func makeValue(r ref) Value {
	var gcPtr *ref
	typeFlag := (r >> 32) & 7
	if (r>>32)&nanHead == nanHead && typeFlag != typeFlagNone {
		gcPtr = new(ref)
		*gcPtr = r
		runtime.SetFinalizer(gcPtr, func(p *ref) {
			finalizeRef(*p)
		})
	}

	return Value{ref: r, gcPtr: gcPtr}
}

func finalizeRef(r ref)

func predefValue(id uint32, typeFlag byte) Value {
	return Value{ref: (nanHead|ref(typeFlag))<<32 | ref(id)}
}

func floatValue(f float64) Value {
	if f == 0 {
		return valueZero
	}
	if f != f {
		return valueNaN
	}
	return Value{ref: *(*ref)(unsafe.Pointer(&f))}
}

// 错误包装了一个JavaScript错误。
type Error struct {
	// 值是基础JavaScript错误值。
	Value
}

// 错误实现错误接口。
func (e Error) Error() string {
	return "JavaScript error: " + e.Get("message").String()
}

var (
	valueUndefined = Value{ref: 0}
	valueNaN       = predefValue(0, typeFlagNone)
	valueZero      = predefValue(1, typeFlagNone)
	valueNull      = predefValue(2, typeFlagNone)
	valueTrue      = predefValue(3, typeFlagNone)
	valueFalse     = predefValue(4, typeFlagNone)
	valueGlobal    = predefValue(5, typeFlagObject)
	jsGo           = predefValue(6, typeFlagObject) // JavaScript中Go类的实例

	objectConstructor = valueGlobal.Get("Object")
	arrayConstructor  = valueGlobal.Get("Array")
)

// Equal根据JavaScript的===运算符报告v和w是否相等。
func (v Value) Equal(w Value) bool {
	return v.ref == w.ref && v.ref != valueNaN.ref
}

// Undefined返回JavaScript值“Undefined”。
func Undefined() Value {
	return valueUndefined
}

// IsUndefined报告v是否为JavaScript值“undefined”。
func (v Value) IsUndefined() bool {
	return v.ref == valueUndefined.ref
}

// Null返回JavaScript值“Null”。
func Null() Value {
	return valueNull
}

// IsNull报告v是否为JavaScript值“null”。
func (v Value) IsNull() bool {
	return v.ref == valueNull.ref
}

// IsNaN报告v是否为JavaScript值“NaN”。
func (v Value) IsNaN() bool {
	return v.ref == valueNaN.ref
}

// Global返回JavaScript全局对象，通常为“窗口”或“全局”。
func Global() Value {
	return valueGlobal
}

// ValueOf将x作为JavaScript值返回：
// 
// /| Go | JavaScript | 
// /|-----------------------------------------------------------------------------------------------------------------------------
// /| js.value |[其值]| 
// /| js.Func | function | 
// /| nil | null | 
// /| boool | 
// /|整数和浮点数| 
// /|字符串| string | 
// |[]接口{}新数组| 
// |映射[字符串]接口{}|新对象| 
// 
// 如果x不是预期类型之一，则会出现恐慌。
func ValueOf(x interface{}) Value {
	switch x := x.(type) {
	case Value: // 应位于包装器之前以避免循环
		return x
	case Wrapper:
		return x.JSValue()
	case nil:
		return valueNull
	case bool:
		if x {
			return valueTrue
		} else {
			return valueFalse
		}
	case int:
		return floatValue(float64(x))
	case int8:
		return floatValue(float64(x))
	case int16:
		return floatValue(float64(x))
	case int32:
		return floatValue(float64(x))
	case int64:
		return floatValue(float64(x))
	case uint:
		return floatValue(float64(x))
	case uint8:
		return floatValue(float64(x))
	case uint16:
		return floatValue(float64(x))
	case uint32:
		return floatValue(float64(x))
	case uint64:
		return floatValue(float64(x))
	case uintptr:
		return floatValue(float64(x))
	case unsafe.Pointer:
		return floatValue(float64(uintptr(x)))
	case float32:
		return floatValue(float64(x))
	case float64:
		return floatValue(x)
	case string:
		return makeValue(stringVal(x))
	case []interface{}:
		a := arrayConstructor.New(len(x))
		for i, s := range x {
			a.SetIndex(i, s)
		}
		return a
	case map[string]interface{}:
		o := objectConstructor.New()
		for k, v := range x {
			o.Set(k, v)
		}
		return o
	default:
		panic("ValueOf: invalid value")
	}
}

func stringVal(x string) ref

// Type表示值的JavaScript类型。
type Type int

const (
	TypeUndefined Type = iota
	TypeNull
	TypeBoolean
	TypeNumber
	TypeString
	TypeSymbol
	TypeObject
	TypeFunction
)

func (t Type) String() string {
	switch t {
	case TypeUndefined:
		return "undefined"
	case TypeNull:
		return "null"
	case TypeBoolean:
		return "boolean"
	case TypeNumber:
		return "number"
	case TypeString:
		return "string"
	case TypeSymbol:
		return "symbol"
	case TypeObject:
		return "object"
	case TypeFunction:
		return "function"
	default:
		panic("bad type")
	}
}

func (t Type) isObject() bool {
	return t == TypeObject || t == TypeFunction
}

// Type返回值v的JavaScript类型。它与JavaScript的typeof运算符
// 类似，只是它返回TypeNull，而不是null的TypeObject。
func (v Value) Type() Type {
	switch v.ref {
	case valueUndefined.ref:
		return TypeUndefined
	case valueNull.ref:
		return TypeNull
	case valueTrue.ref, valueFalse.ref:
		return TypeBoolean
	}
	if v.isNumber() {
		return TypeNumber
	}
	typeFlag := (v.ref >> 32) & 7
	switch typeFlag {
	case typeFlagObject:
		return TypeObject
	case typeFlagString:
		return TypeString
	case typeFlagSymbol:
		return TypeSymbol
	case typeFlagFunction:
		return TypeFunction
	default:
		panic("bad type flag")
	}
}

// Get返回值为v的JavaScript属性p。
// 如果v不是JavaScript对象，它会惊慌失措。
func (v Value) Get(p string) Value {
	if vType := v.Type(); !vType.isObject() {
		panic(&ValueError{"Value.Get", vType})
	}
	r := makeValue(valueGet(v.ref, p))
	runtime.KeepAlive(v)
	return r
}

func valueGet(v ref, p string) ref

// Set将值v的JavaScript属性p设置为值（x）。
// 如果v不是JavaScript对象，它会惊慌失措。
func (v Value) Set(p string, x interface{}) {
	if vType := v.Type(); !vType.isObject() {
		panic(&ValueError{"Value.Set", vType})
	}
	xv := ValueOf(x)
	valueSet(v.ref, p, xv.ref)
	runtime.KeepAlive(v)
	runtime.KeepAlive(xv)
}

func valueSet(v ref, p string, x ref)

// Delete删除值v的JavaScript属性p。
// 如果v不是JavaScript对象，它会惊慌失措。
func (v Value) Delete(p string) {
	if vType := v.Type(); !vType.isObject() {
		panic(&ValueError{"Value.Delete", vType})
	}
	valueDelete(v.ref, p)
	runtime.KeepAlive(v)
}

func valueDelete(v ref, p string)

// Index返回值为v的JavaScript索引i。
// 如果v不是JavaScript对象，它会惊慌失措。
func (v Value) Index(i int) Value {
	if vType := v.Type(); !vType.isObject() {
		panic(&ValueError{"Value.Index", vType})
	}
	r := makeValue(valueIndex(v.ref, i))
	runtime.KeepAlive(v)
	return r
}

func valueIndex(v ref, i int) ref

// SetIndex将值v的JavaScript索引i设置为值（x）。
// 如果v不是JavaScript对象，它会惊慌失措。
func (v Value) SetIndex(i int, x interface{}) {
	if vType := v.Type(); !vType.isObject() {
		panic(&ValueError{"Value.SetIndex", vType})
	}
	xv := ValueOf(x)
	valueSetIndex(v.ref, i, xv.ref)
	runtime.KeepAlive(v)
	runtime.KeepAlive(xv)
}

func valueSetIndex(v ref, i int, x ref)

func makeArgs(args []interface{}) ([]Value, []ref) {
	argVals := make([]Value, len(args))
	argRefs := make([]ref, len(args))
	for i, arg := range args {
		v := ValueOf(arg)
		argVals[i] = v
		argRefs[i] = v.ref
	}
	return argVals, argRefs
}

// Length返回v的JavaScript属性“Length”。
// 如果v不是JavaScript对象，它会惊慌失措。
func (v Value) Length() int {
	if vType := v.Type(); !vType.isObject() {
		panic(&ValueError{"Value.SetIndex", vType})
	}
	r := valueLength(v.ref)
	runtime.KeepAlive(v)
	return r
}

func valueLength(v ref) int

// Call使用给定参数对值为v的方法m进行JavaScript调用。
// 如果v没有方法m，它就会恐慌。
// 参数根据函数的ValueOf映射到JavaScript值。
func (v Value) Call(m string, args ...interface{}) Value {
	argVals, argRefs := makeArgs(args)
	res, ok := valueCall(v.ref, m, argRefs)
	runtime.KeepAlive(v)
	runtime.KeepAlive(argVals)
	if !ok {
		if vType := v.Type(); !vType.isObject() { // 检查此处以避免成功案例中的开销
			panic(&ValueError{"Value.Call", vType})
		}
		if propType := v.Get(m).Type(); propType != TypeFunction {
			panic("syscall/js: Value.Call: property " + m + " is not a function, got " + propType.String())
		}
		panic(Error{makeValue(res)})
	}
	return makeValue(res)
}

func valueCall(v ref, m string, args []ref) (ref, bool)

// Invoke使用给定参数执行值v的JavaScript调用。
// 如果v不是JavaScript函数，它会惊慌失措。
// 参数根据函数的ValueOf映射到JavaScript值。
func (v Value) Invoke(args ...interface{}) Value {
	argVals, argRefs := makeArgs(args)
	res, ok := valueInvoke(v.ref, argRefs)
	runtime.KeepAlive(v)
	runtime.KeepAlive(argVals)
	if !ok {
		if vType := v.Type(); vType != TypeFunction { // 检查此处以避免成功案例中的开销
			panic(&ValueError{"Value.Invoke", vType})
		}
		panic(Error{makeValue(res)})
	}
	return makeValue(res)
}

func valueInvoke(v ref, args []ref) (ref, bool)

// New使用JavaScript的“New”运算符，值为v作为构造函数和给定参数。
// 如果v不是JavaScript函数，它就会恐慌。
// 参数根据函数的ValueOf映射到JavaScript值。
func (v Value) New(args ...interface{}) Value {
	argVals, argRefs := makeArgs(args)
	res, ok := valueNew(v.ref, argRefs)
	runtime.KeepAlive(v)
	runtime.KeepAlive(argVals)
	if !ok {
		if vType := v.Type(); vType != TypeFunction { // 检查此处以避免成功案例中的开销
			panic(&ValueError{"Value.Invoke", vType})
		}
		panic(Error{makeValue(res)})
	}
	return makeValue(res)
}

func valueNew(v ref, args []ref) (ref, bool)

func (v Value) isNumber() bool {
	return v.ref == valueZero.ref ||
		v.ref == valueNaN.ref ||
		(v.ref != valueUndefined.ref && (v.ref>>32)&nanHead != nanHead)
}

func (v Value) float(method string) float64 {
	if !v.isNumber() {
		panic(&ValueError{method, v.Type()})
	}
	if v.ref == valueZero.ref {
		return 0
	}
	return *(*float64)(unsafe.Pointer(&v.ref))
}

// Float将值v作为float64返回。
// 如果v不是一个JavaScript数字，它就会恐慌。
func (v Value) Float() float64 {
	return v.float("Value.Float")
}

// Int返回被截断为Int的值v。
// 如果v不是一个JavaScript数字，它将陷入恐慌。
func (v Value) Int() int {
	return int(v.float("Value.Int"))
}

// Bool将值v作为Bool返回。
// 如果v不是JavaScript布尔值，它就会恐慌。
func (v Value) Bool() bool {
	switch v.ref {
	case valueTrue.ref:
		return true
	case valueFalse.ref:
		return false
	default:
		panic(&ValueError{"Value.Bool", v.Type()})
	}
}

// Truthy返回值v的JavaScript“truthyness”。在JavaScript中，
// false、0、“、null、undefined和NaN都是“falsy”，其他所有内容都是
// “truthy”。看见https:
func (v Value) Truthy() bool {
	switch v.Type() {
	case TypeUndefined, TypeNull:
		return false
	case TypeBoolean:
		return v.Bool()
	case TypeNumber:
		return v.ref != valueNaN.ref && v.ref != valueZero.ref
	case TypeString:
		return v.String() != ""
	case TypeSymbol, TypeFunction, TypeObject:
		return true
	default:
		panic("bad type")
	}
}

// String将值v作为字符串返回。
// String是一个特例，因为Go的String方法约定。与其他getter不同，
// 如果v的类型不是TypeString，它不会死机。相反，它返回格式为“<T>”
// 或“<T:V>”的字符串，其中T是V的类型，V是V值的字符串表示形式。
func (v Value) String() string {
	switch v.Type() {
	case TypeString:
		return jsString(v)
	case TypeUndefined:
		return "<undefined>"
	case TypeNull:
		return "<null>"
	case TypeBoolean:
		return "<boolean: " + jsString(v) + ">"
	case TypeNumber:
		return "<number: " + jsString(v) + ">"
	case TypeSymbol:
		return "<symbol>"
	case TypeObject:
		return "<object>"
	case TypeFunction:
		return "<function>"
	default:
		panic("bad type")
	}
}

func jsString(v Value) string {
	str, length := valuePrepareString(v.ref)
	runtime.KeepAlive(v)
	b := make([]byte, length)
	valueLoadString(str, b)
	finalizeRef(str)
	return string(b)
}

func valuePrepareString(v ref) (ref, int)

func valueLoadString(v ref, b []byte)

// InstanceOf根据JavaScript的InstanceOf运算符报告v是否是t类型的实例。
func (v Value) InstanceOf(t Value) bool {
	r := valueInstanceOf(v.ref, t.ref)
	runtime.KeepAlive(v)
	runtime.KeepAlive(t)
	return r
}

func valueInstanceOf(v ref, t ref) bool

// 在
// 不支持Value方法的值上调用Value方法时，会发生Value错误。在每种方法的描述中都记录了此类案例。
type ValueError struct {
	Method string
	Type   Type
}

func (e *ValueError) Error() string {
	return "syscall/js: call of " + e.Method + " on " + e.Type.String()
}

// CopyBytesToGo将字节从src复制到dst。
// 如果src不是Uint8Array或Uint8ClampedArray，它就会恐慌。
// 返回复制的字节数，即src和dst的最小长度。
func CopyBytesToGo(dst []byte, src Value) int {
	n, ok := copyBytesToGo(dst, src.ref)
	runtime.KeepAlive(src)
	if !ok {
		panic("syscall/js: CopyBytesToGo: expected src to be an Uint8Array or Uint8ClampedArray")
	}
	return n
}

func copyBytesToGo(dst []byte, src ref) (int, bool)

// CopyBytesToJS将字节从src复制到dst。
// 如果dst不是uint8数组或uint8 clampedarray，它就会恐慌。
// 返回复制的字节数，它是src和dst的最小长度。
func CopyBytesToJS(dst Value, src []byte) int {
	n, ok := copyBytesToJS(dst.ref, src)
	runtime.KeepAlive(dst)
	if !ok {
		panic("syscall/js: CopyBytesToJS: expected dst to be an Uint8Array or Uint8ClampedArray")
	}
	return n
}

func copyBytesToJS(dst ref, src []byte) (int, bool)
